package de.hs_mannheim.vem.ajchat;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public abstract class ServerApi {
	private final ServerSocket	sock;
	final ThreadPoolExecutor	pool;

	public ServerApi(final InetSocketAddress socket) throws IOException {
		this(new ServerSocket(socket.getPort(), 100, socket.getAddress()));
	}

	private ServerApi(final ServerSocket sock) {
		this.sock = sock;
		pool = new ThreadPoolExecutor(20, 60, 10000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(
				1000));

		pool.execute(new Runnable() {
			@Override
			public void run() {
				Socket con;
				try {
					while ((con = sock.accept()) != null) {
						final Socket curCon = con;
						pool.execute(new Runnable() {
							@Override
							public void run() {
								InputStream is = null;
								try {
									is = curCon.getInputStream();
									final ObjectInputStream ois = new ObjectInputStream(is);
									receive(ois, curCon.getRemoteSocketAddress());
								} catch (final Exception e) {
									handleException(e);
								} finally {
									try {
										if (is != null) {
											is.close();
										}
										curCon.close();
									} catch (final IOException e) {
										// Exception irrelevant
									}
								}
							}
						});
					}
				} catch (final IOException e) {
					// This can also happen on forced socket shutdown
					if (!pool.isShutdown()) {
						handleException(e);
					}
				} finally {
					try {
						sock.close();
					} catch (final Exception e) {
						// Exception irrelevant
					}
				}
			}
		});
	}

	public final void stopListening() {
		try {
			pool.shutdown();
			sock.close(); // This causes the Server-Api to throw an exception
		} catch (final IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public String toString() {
		return sock.toString();
	}

	protected abstract void handleException(Exception e);

	protected abstract void receive(ObjectInputStream ois, SocketAddress addr) throws Exception;
}
